Skip to content

React Router 原理

React Router 是 React 官方提供的路由管理器,它可以帮助我们管理应用的路由,实现不同 URL 对应的不同组件的渲染。

工作原理

我们先看一段 React Router 的代码:

jsx
import React from'react';
import { BrowserRouter as Router, Switch, Route } from'react-router-dom';


function App() {
  return (
    <Router>
      <Switch>
        <Route exact path="/" component={Home} />
        <Route path="/users" component={Users} />
        <Route path="/users/:id" component={User} />
        <Route path="/users/:id/edit" component={UserEdit} />
      </Switch>
    </Router>
  );

}

export default App;

React Router 的工作原理如下:

  1. 首先,我们需要定义好路由规则,比如我们有如下路由规则:
/users
/users/:id
/users/:id/edit
  1. 然后,我们需要在应用的入口文件中,使用 React Router 的 Router 组件,将路由规则和对应的组件进行绑定。

  2. 当用户访问 /users 页面时,React Router 会匹配到对应的路由规则,并渲染 Users 组件。

  3. 当用户访问 /users/:id 页面时,React Router 会匹配到对应的路由规则,并渲染 User 组件,并将 :id 作为参数传递给 User 组件。

  4. 当用户访问 /users/:id/edit 页面时,React Router 会匹配到对应的路由规则,并渲染 UserEdit 组件,并将 :id 作为参数传递给 UserEdit 组件。

React Router 的路由匹配规则是基于路径的,因此,我们需要确保路径的正确性。

  1. 当用户访问 /users/123/edit 页面时,React Router 会匹配到 /users/:id/edit 路由规则,并渲染 UserEdit 组件,并将 123 作为参数传递给 UserEdit 组件。

代码实现

1. Router 组件

Router 组件是 React Router 的核心组件,它负责管理应用的路由,并渲染对应的组件。

jsx
  export default class BrowserRouter extends React.Component {
    constructor(props) {
      this.history = createBrowserHistory();
    }
    render() {
      const 
      return (
        <RouterContext.Provider value={{history: this.history, location: this.history.location, staticContext: undefined}}>
          {this.props.children}
        </RouterContext.Provider>
      );
    }
  }

2.Route 组件

jsx
export default class Route extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      match: props.computeMatch(props.location.pathname)
    };
  }
  componentDidMount() {
    const { history } = this.props;
    const unlisten = history.listen((location) => {
      this.setState({
        match: this.props.computeMatch(location.pathname)
      });
    });
    this.setState({
      unlisten
    });
  }
  componentWillUnmount() {
    this.state.unlisten();
  }
  render() {
    const { component: Component, render, children,computedMatch } = this.props;
    if (!computedMatch) {
      return null;
    }
    
   const match = computedMatch|| matchPath(this.props.location.pathname, this.props);
    if (match) {
      if (Component) {
        return <Component {...this.props} />;
      } else if (render) {
        return render({...this.props, match });
      } else if (children) {
        return children({...this.props, match });
      }
    }
    return null;
  }

}

3.Switch 组件

jsx
export default class Switch extends React.Component {
  render() {
    const { location, children } = this.props;
    let match = null;
    React.Children.forEach(children, (element) => {
      // 如果没有 match 才执行计算
      if (match == null && React.isValidElement(element)) {
        const { path, exact, strict, sensitive, from } = element.props;
        const pathname = getFullPath(location.pathname, from);
        if (path) {
          match = matchPath(pathname, { path, exact, strict, sensitive });
        } else {
          match = pathMatch(pathname);
        }
      }
    });
    return match ? React.cloneElement(React.Children.only(children), { computedMatch: match }) : null;
  }
}

总结

Router 组件是负责上下文的注入,还有统一前缀等配置,Route 组件可以根据 path 进行匹配,并且内部提供了 compuMatch 方法,由外部控制是否渲染,Switch 组件可以根据路由匹配到的第一个组件进行渲染。